/*	#	 FileName	: main.c
#	 Created	: Tue 15 Apr 2025 03:22:33 PM CST
 *****************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>

#include "zlog.h"
#include "cJSON.h"

#define ZLOG_PATH	"../cfg/zlog.conf"
#define cjson_debug(...) zlog_debug(zc, __VA_ARGS__)
#define cjson_info(...) zlog_info(zc, __VA_ARGS__)
#define cjson_warn(...) zlog_warn(zc, __VA_ARGS__)
#define cjson_error(...) zlog_error(zc, __VA_ARGS__)

typedef struct {
	int cell_id;
	int cfg_state;
	int rf_state;
	int async_state;
}cell_info_t;

typedef struct {
	char 	dev_id[64];
	char 	dev_name[64];
	int 	boardtemp[4];
	int 	voltage[4];
	int 	cpu;
	int 	memory;
	int 	uptime;
	int8_t 	time_sync_state;
	int8_t 	gps_sync_state;
	int8_t 	dev_state;
	int8_t 	kickoff;
	char 	wifi_ip[64];
	char 	wifi_ssid[64];
	int8_t 	work_mode;
	int 	fan_speed;
	int 	fan_rpm;
	int 	gnss_select;
	int 	latitude;
	int 	longitude;

	cell_info_t cell_info[6];
}json_str_t;

static zlog_category_t *zc;

/* 
 * 函数说明：构造json
 * 参数：无
 * 返回值：
 * 		成功：返回构造的json字符串
 *		失败：返回NULL
 */
static char *build_json(void);

/* 
 * 函数说明：构造json,使用宏函数来实现
 * 参数：无
 * 返回值：
 * 		成功：返回构造的json字符串
 *		失败：返回NULL
 */
static char *build_json_2(void);

/* 
 * 函数说明：解析json
 * 参数：
 *		str:	要解析的json字符串
 *		info：	存放解析后的信息
 * 返回值：
 * 		成功：0
 *		失败：-1
 */
static int parse_json(char *str, json_str_t *info);

/* 
 * 函数说明：解析json, 使用宏函数实现
 * 参数：
 *		str:	要解析的json字符串
 *		info：	存放解析后的信息
 * 返回值：
 * 		成功：0
 *		失败：-1
 */
static int parse_json_2(char *str, json_str_t *info);

/* 
 * 函数说明：修改已有的json，包括增删改
 * 参数：
 *		str:	要修改的json字符串
 * 返回值：
 * 		成功：返回修改后的json字符串
 *		失败：返回NULL
 */
static char *opt_json(char *str, json_str_t *info);

int main(int argc, const char *argv[])
{
	int ret = 0;
	char *json_str;
	char *update_str;
	json_str_t info;

	/* 初始化zlog */
	ret = zlog_init(ZLOG_PATH);
	if (ret)
	{
		fprintf(stderr, "zlog init failed\n");
		return -1;
	}

	zc = zlog_get_category("my_cat");
	if (!zc)
	{
		fprintf(stderr, "get cat failed\n");
		ret = -1;
		goto end;
	}
	cjson_info("hello zlog");

#if 0
	json_str = build_json();
#else
	json_str = build_json_2();
#endif
	if (json_str == NULL)
	{
		cjson_error("build json failed");
		ret = -1;
		goto end;
	}
	printf("json_str: \n%s\n", json_str);

	puts("\n**************************************************\n");
	printf("parse json: %s\n", json_str);
#if 0
	ret = parse_json(json_str, &info);
#else
	ret = parse_json_2(json_str, &info);
#endif
	if (ret < 0)
	{
		cjson_info("parse_json failed");
		ret = -1;
		goto end;
	}

	puts("\n**************************************************\n");
	update_str = opt_json(json_str, &info);
	if (update_str == NULL)
	{
		cjson_error("opt json failed");
		ret = -1;
		goto end;
	}
	puts("update json: %s");
	printf("%s\n", update_str);

	ret = 0;
end:
	free(json_str);
	json_str = NULL;
	free(update_str);
	update_str = NULL;
	zlog_fini();
	return ret;
}

static char *build_json(void)
{/*{{{*/
	cJSON *root = NULL, *dev_info = NULL;		// obj
	cJSON *boardtemp = NULL, *voltage = NULL;	// arr
	cJSON *cell_info = NULL, *cell_infos = NULL;
	int boardtemp_value[] = {40, 20, 0, 0};
	int voltage_value[] = {1, 1, 0, 0};
	int i = 0;
	char *str = NULL;

	root = cJSON_CreateObject();
	if (root == NULL)
	{
		cjson_error("cJSON_CreateObject root failed");
		goto end;
	}

	dev_info = cJSON_CreateObject();
	if (dev_info == NULL)
	{
		cjson_error("cJSON_CreateObject dev_info failed");
		goto end;
	}

	// 添加dev_info到root下
	cJSON_AddItemToObject(root, "dev_info", dev_info);

#define STRTOOBJ(x, y, z)	\
	do { \
		if (cJSON_AddStringToObject(x, y, z) == NULL) \
		{	\
			cjson_error("cJSON_AddStringToObject failed, key: dev_id %s, value: %s", y, z); \
			str = NULL; \
			goto end;	\
		}	\
	}while(0)

#define NUMTOOBJ(x, y, z)	\
	do { \
		if (cJSON_AddNumberToObject(x, y, z) == NULL) \
		{	\
			cjson_error("cJSON_AddNumberToObject failed, key: dev_id %s, value: %d", y, z); \
			str = NULL; \
			goto end;	\
		}	\
	}while(0)

	if (cJSON_AddStringToObject(dev_info, "dev_id", "xxx") == NULL)
	{
		cjson_error("cJSON_AddStringToObject failed, key: dev_id, value: xxx");
		goto end;
	}

	if (cJSON_AddStringToObject(dev_info, "dev_name", "xxx") == NULL)
	{
		cjson_error("cJSON_AddStringToObject failed, key: dev_name, value: xxx");
		goto end;
	}

	boardtemp = cJSON_CreateIntArray(boardtemp_value, sizeof(boardtemp_value) / sizeof(boardtemp_value[0]));
	if (boardtemp == NULL)
	{
		cjson_error("cJSON_CreateIntArray boardtemp failed");
		goto end;
	}

	cJSON_AddItemToObject(dev_info, "boardtemp", boardtemp);

	voltage = cJSON_CreateIntArray(voltage_value, sizeof(voltage_value) / sizeof(voltage_value[0]));
	if (voltage == NULL)
	{
		cjson_error("cJSON_CreateIntArray voltage failed");
		goto end;
	}

	cJSON_AddItemToObject(dev_info, "voltage", voltage);

	if (cJSON_AddNumberToObject(dev_info, "cpu", 5) == NULL)
	{
		cjson_error("cJSON_AddNumberToObject failed, key: cpu, value: 5");
		goto end;
	}

	if (cJSON_AddNumberToObject(dev_info, "memory", 29) == NULL)
	{
		cjson_error("cJSON_AddNumberToObject failed, key: memory, value: 29");
		goto end;
	}

	if (cJSON_AddNumberToObject(dev_info, "uptime", 511342) == NULL)
	{
		cjson_error("cJSON_AddNumberToObject failed, key: uptime, value: 511342");
		goto end;
	}

	if (cJSON_AddNumberToObject(dev_info, "time_sync_state", 1) == NULL)
	{
		cjson_error("cJSON_AddNumberToObject failed, key: time_sync_state, value: 1");
		goto end;
	}

	if (cJSON_AddNumberToObject(dev_info, "gps_sync_state", 1) == NULL)
	{
		cjson_error("cJSON_AddNumberToObject failed, key: gps_sync_state, value: 1");
		goto end;
	}

	if (cJSON_AddNumberToObject(dev_info, "dev_state", 0) == NULL)
	{
		cjson_error("cJSON_AddNumberToObject failed, key: dev_state, value: 0");
		goto end;
	}

	if (cJSON_AddNumberToObject(dev_info, "kickoff", 0) == NULL)
	{
		cjson_error("cJSON_AddNumberToObject failed, key: kickoff, value: 0");
		goto end;
	}

	if (cJSON_AddStringToObject(dev_info, "wifi_ip", "192.168.43.100") == NULL)
	{
		cjson_error("cJSON_AddStringToObject failed, key: wifi_ip, value: 192.168.43.100");
		goto end;
	}

	if (cJSON_AddStringToObject(dev_info, "wifi_ssid", "simdo") == NULL)
	{
		cjson_error("cJSON_AddStringToObject failed, key: wifi_ssid, value: simdo");
		goto end;
	}

	if (cJSON_AddNumberToObject(dev_info, "work_mode", 2) == NULL)
	{
		cjson_error("cJSON_AddNumberToObject failed, key: work_mode, value: 2");
		goto end;
	}

	if (cJSON_AddNumberToObject(dev_info, "fan_speed", 23) == NULL)
	{
		cjson_error("cJSON_AddNumberToObject failed, key: fan_speed, value: 23");
		goto end;
	}

	if (cJSON_AddNumberToObject(dev_info, "fan_rpm", 45) == NULL)
	{
		cjson_error("cJSON_AddNumberToObject failed, key: fan_rpm, value: 45");
		goto end;
	}

	if (cJSON_AddNumberToObject(dev_info, "gnss_select", 67) == NULL)
	{
		cjson_error("cJSON_AddNumberToObject failed, key: gnss_select, value: 67");
		goto end;
	}

	if (cJSON_AddNumberToObject(dev_info, "latitude", 89) == NULL)
	{
		cjson_error("cJSON_AddNumberToObject failed, key: latitude, value: 89");
		goto end;
	}

	if (cJSON_AddNumberToObject(dev_info, "longitude", 10) == NULL)
	{
		cjson_error("cJSON_AddNumberToObject failed, key: longitude, value: 10");
		goto end;
	}

	cell_infos = cJSON_AddArrayToObject(root, "cell_info");
	if (cell_infos == NULL)
	{
		cjson_error("cJSON_AddArrayToObject cell_infos failed");
		goto end;
	}

	for (i = 0; i < 6; i++)
	{
		cell_info = cJSON_CreateObject();

		if (cJSON_AddNumberToObject(cell_info, "cell_id", i) == NULL)
		{
			cjson_error("cJSON_AddNumberToObject failed, key: cell_id, value: %d", i);
			goto end;
		}

		if (cJSON_AddNumberToObject(cell_info, "cfg_state", 0) == NULL)
		{
			cjson_error("cJSON_AddNumberToObject failed, key: cfg_state, value: 0");
			goto end;
		}

		if (cJSON_AddNumberToObject(cell_info, "rf_state", 0) == NULL)
		{
			cjson_error("cJSON_AddNumberToObject failed, key: rf_state, value: 0");
			goto end;
		}

		if (cJSON_AddNumberToObject(cell_info, "async_state", 0) == NULL)
		{
			cjson_error("cJSON_AddNumberToObject failed, key: async_state, value: 0");
			goto end;
		}

		cJSON_AddItemToArray(cell_infos, cell_info);
	}


	str = cJSON_Print(root);
	if (str == NULL)
	{
		cjson_error("cJSON_Print failed");
		goto end;
	}
	
end:	
	if (root != NULL)
		cJSON_Delete(root);
	return str;
}/*}}}*/

static char *build_json_2(void)
{/*{{{*/
	cJSON *root = NULL, *dev_info = NULL;		// obj
	cJSON *boardtemp = NULL, *voltage = NULL;	// arr
	cJSON *cell_info = NULL, *cell_infos = NULL;
	int boardtemp_value[] = {40, 20, 0, 0};
	int voltage_value[] = {1, 1, 0, 0};
	int i = 0;
	char *str = NULL;

	root = cJSON_CreateObject();
	if (root == NULL)
	{
		cjson_error("cJSON_CreateObject root failed");
		goto end;
	}

	dev_info = cJSON_CreateObject();
	if (dev_info == NULL)
	{
		cjson_error("cJSON_CreateObject dev_info failed");
		goto end;
	}

	// 添加dev_info到root下
	cJSON_AddItemToObject(root, "dev_info", dev_info);

#define STRTOOBJ(x, y, z)	\
	do { \
		if (cJSON_AddStringToObject(x, y, z) == NULL) \
		{	\
			cjson_error("cJSON_AddStringToObject failed, key: dev_id %s, value: %s", y, z); \
			str = NULL; \
			goto end;	\
		}	\
	}while(0)

#define NUMTOOBJ(x, y, z)	\
	do { \
		if (cJSON_AddNumberToObject(x, y, z) == NULL) \
		{	\
			cjson_error("cJSON_AddNumberToObject failed, key: dev_id %s, value: %d", y, z); \
			str = NULL; \
			goto end;	\
		}	\
	}while(0)

	STRTOOBJ(dev_info, "dev_id", "xxx");
	STRTOOBJ(dev_info, "dev_name", "xxx");
	NUMTOOBJ(dev_info, "cpu", 5);
	NUMTOOBJ(dev_info, "memory", 29);
	NUMTOOBJ(dev_info, "uptime", 511342);

	boardtemp = cJSON_CreateIntArray(boardtemp_value, sizeof(boardtemp_value) / sizeof(boardtemp_value[0]));
	if (boardtemp == NULL)
	{
		cjson_error("cJSON_CreateIntArray boardtemp failed");
		goto end;
	}

	cJSON_AddItemToObject(dev_info, "boardtemp", boardtemp);

	voltage = cJSON_CreateIntArray(voltage_value, sizeof(voltage_value) / sizeof(voltage_value[0]));
	if (voltage == NULL)
	{
		cjson_error("cJSON_CreateIntArray voltage failed");
		goto end;
	}

	cJSON_AddItemToObject(dev_info, "voltage", voltage);

	if (cJSON_AddNumberToObject(dev_info, "memory", 29) == NULL)
	{
		cjson_error("cJSON_AddNumberToObject failed, key: memory, value: 29");
		goto end;
	}

	NUMTOOBJ(dev_info, "time_sync_state", 1);
	NUMTOOBJ(dev_info, "gps_sync_state", 1);
	NUMTOOBJ(dev_info, "dev_state", 0);
	NUMTOOBJ(dev_info, "kickoff", 0);
	STRTOOBJ(dev_info, "wifi_ip", "192.168.43.100");
	STRTOOBJ(dev_info, "wifi_ssid", "simdo");
	NUMTOOBJ(dev_info, "work_mode", 2);
	NUMTOOBJ(dev_info, "fan_speed", 23);
	NUMTOOBJ(dev_info, "fan_rpm", 45);
	NUMTOOBJ(dev_info, "gnss_select", 67);
	NUMTOOBJ(dev_info, "latitude", 89);
	NUMTOOBJ(dev_info, "longitude", 10);

	cell_infos = cJSON_AddArrayToObject(root, "cell_info");
	if (cell_infos == NULL)
	{
		cjson_error("cJSON_AddArrayToObject cell_infos failed");
		goto end;
	}

	for (i = 0; i < 6; i++)
	{
		cell_info = cJSON_CreateObject();

		NUMTOOBJ(cell_info, "cell_id", i);
		NUMTOOBJ(cell_info, "cfg_state", 0);
		NUMTOOBJ(cell_info, "rf_state", 0);
		NUMTOOBJ(cell_info, "async_state", 0);

		cJSON_AddItemToArray(cell_infos, cell_info);
	}


	str = cJSON_Print(root);
	if (str == NULL)
	{
		cjson_error("cJSON_Print failed");
		goto end;
	}
	
end:	
	if (root != NULL)
		cJSON_Delete(root);
#undef NUMTOOBJ
#undef STRTOOBJ
	return str;
}/*}}}*/

static int parse_json(char *str, json_str_t *info)
{
	cJSON *root, *dev_info, *obj;
	cJSON *boardtemps, *boardtemp;
	cJSON *voltages, *voltage;
	cJSON *cell_infos, *cell_info;
	int ret = 0, i = 0;
	const char *error_ptr;

	root = cJSON_Parse(str);
	if (root == NULL)
	{
		cjson_error("cJSON_Parse failed");
		ret = -1;
		goto end;
	}

	// dev_info = cJSON_GetObjectItem(root, "dev_info")		// 该函数不区分大小写，可以匹配 Dev_INFO 等
	dev_info = cJSON_GetObjectItemCaseSensitive(root, "dev_info");	// 严格区分大小写
	if (dev_info == NULL)
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive dev_info failed");
		ret = -1;
		goto end;
	}

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "dev_id");
	if (!cJSON_IsString(obj) || (obj->valuestring == NULL))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive dev_id failed");
		ret = -1;
		goto end;
	}
	memcpy(info->dev_id, obj->valuestring, strlen(obj->valuestring) + 1);	// 拷贝字符串以及'\0'
	printf("dev_id: %s\n", info->dev_id);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "dev_name");
	if (!cJSON_IsString(obj) || (obj->valuestring == NULL))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive dev_name failed");
		ret = -1;
		goto end;
	}
	memcpy(info->dev_name, obj->valuestring, strlen(obj->valuestring) + 1);
	printf("dev_name: %s\n", info->dev_name);

	boardtemps = cJSON_GetObjectItemCaseSensitive(dev_info, "boardtemp");
	if (boardtemps == NULL)
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive boardtemp failed");
		ret = -1;
		goto end;
	}
	i = 0;
	cJSON_ArrayForEach(boardtemp, boardtemps)	// 遍历数组
	{
		info->boardtemp[i++] = boardtemp->valueint;
		printf("boardtemp[%d]: %d\n", i, boardtemp->valueint);
	}

	voltages = cJSON_GetObjectItemCaseSensitive(dev_info, "voltage");
	if (voltages == NULL)
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive voltage failed");
		ret = -1;
		goto end;
	}
	i = 0;
	cJSON_ArrayForEach(voltage, voltages)
	{
		info->voltage[i++] = voltage->valueint;
		printf("voltage[%d]: %d\n", i, voltage->valueint);
	}

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "cpu");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive cpu failed");
		goto end;
	}
	info->cpu = obj->valueint;
	printf("cpu: %d\n", info->cpu);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "memory");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive memory failed");
		goto end;
	}
	info->memory = obj->valueint;
	printf("memory: %d\n", info->memory);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "uptime");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive uptime failed");
		goto end;
	}
	info->uptime = obj->valueint;
	printf("uptime: %d\n", info->uptime);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "time_sync_state");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive time_sync_state failed");
		goto end;
	}
	info->time_sync_state = obj->valueint;
	printf("time_sync_state: %d\n", info->time_sync_state);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "gps_sync_state");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive gps_sync_state failed");
		goto end;
	}
	info->gps_sync_state = obj->valueint;
	printf("gps_sync_state: %d\n", info->gps_sync_state);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "dev_state");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive dev_state failed");
		goto end;
	}
	info->dev_state = obj->valueint;
	printf("dev_state: %d\n", info->dev_state);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "kickoff");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive kickoff failed");
		goto end;
	}
	info->kickoff = obj->valueint;
	printf("kickoff: %d\n", info->kickoff);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "wifi_ip");
	if (!cJSON_IsString(obj) || (obj->valuestring == NULL))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive wifi_ip failed");
		ret = -1;
		goto end;
	}
	memcpy(info->wifi_ip, obj->valuestring, strlen(obj->valuestring) + 1);
	printf("wifi_ip: %s\n", info->wifi_ip);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "wifi_ssid");
	if (!cJSON_IsString(obj) || (obj->valuestring == NULL))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive wifi_ssid failed");
		ret = -1;
		goto end;
	}
	memcpy(info->wifi_ssid, obj->valuestring, strlen(obj->valuestring) + 1);
	printf("wifi_ssid: %s\n", info->wifi_ssid);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "work_mode");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive work_mode failed");
		goto end;
	}
	info->work_mode = obj->valueint;
	printf("work_mode: %d\n", info->work_mode);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "fan_speed");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive fan_speed failed");
		goto end;
	}
	info->fan_speed = obj->valueint;
	printf("fan_speed: %d\n", info->fan_speed);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "fan_rpm");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive fan_rpm failed");
		goto end;
	}
	info->fan_rpm = obj->valueint;
	printf("fan_rpm: %d\n", info->fan_rpm);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "gnss_select");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive gnss_select failed");
		goto end;
	}
	info->gnss_select = obj->valueint;
	printf("gnss_select: %d\n", info->gnss_select);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "latitude");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive latitude failed");
		goto end;
	}
	info->latitude = obj->valueint;
	printf("latitude: %d\n", info->latitude);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "longitude");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive longitude failed");
		goto end;
	}
	info->longitude = obj->valueint;
	printf("longitude: %d\n", info->longitude);

	cell_infos = cJSON_GetObjectItemCaseSensitive(root, "cell_info");
	if (cell_infos == NULL)
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive cell_info failed");
		ret = -1;
		goto end;
	}
	i = 0;
	cJSON_ArrayForEach(cell_info, cell_infos)
	{
		obj = cJSON_GetObjectItemCaseSensitive(cell_info, "cell_id");
		if (!cJSON_IsNumber(obj))
		{
			cjson_error("cJSON_GetObjectItemCaseSensitive cell_id failed");
			goto end;
		}
		info->cell_info[i].cell_id = obj->valueint;
		printf("[%d] cell_id: %d\n", i, info->cell_info[i].cell_id);

		obj = cJSON_GetObjectItemCaseSensitive(cell_info, "cfg_state");
		if (!cJSON_IsNumber(obj))
		{
			cjson_error("cJSON_GetObjectItemCaseSensitive cfg_state failed");
			goto end;
		}
		info->cell_info[i].cfg_state = obj->valueint;
		printf("[%d] cfg_state: %d\n", i, info->cell_info[i].cfg_state);

		obj = cJSON_GetObjectItemCaseSensitive(cell_info, "rf_state");
		if (!cJSON_IsNumber(obj))
		{
			cjson_error("cJSON_GetObjectItemCaseSensitive rf_state failed");
			goto end;
		}
		info->cell_info[i].rf_state = obj->valueint;
		printf("[%d] rf_state: %d\n", i, info->cell_info[i].cfg_state);

		obj = cJSON_GetObjectItemCaseSensitive(cell_info, "async_state");
		if (!cJSON_IsNumber(obj))
		{
			cjson_error("cJSON_GetObjectItemCaseSensitive async_state failed");
			goto end;
		}
		info->cell_info[i].async_state = obj->valueint;
		printf("[%d] async_state: %d\n", i, info->cell_info[i].cfg_state);

		i++;
	}

	ret = 0;
end:
	error_ptr = cJSON_GetErrorPtr();
	if (error_ptr != NULL)
	{
		cjson_error("cause: %s", error_ptr);
	}
	return ret;	
}

static int parse_json_2(char *str, json_str_t *info)
{
	cJSON *root, *dev_info, *obj;
	cJSON *boardtemps, *boardtemp;
	cJSON *voltages, *voltage;
	cJSON *cell_infos, *cell_info;
	int ret = 0, i = 0;
	const char *error_ptr;

	root = cJSON_Parse(str);
	if (root == NULL)
	{
		cjson_error("cJSON_Parse failed");
		ret = -1;
		goto end;
	}

	// dev_info = cJSON_GetObjectItem(root, "dev_info")		// 该函数不区分大小写，可以匹配 Dev_INFO 等
	dev_info = cJSON_GetObjectItemCaseSensitive(root, "dev_info");	// 严格区分大小写
	if (dev_info == NULL)
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive dev_info failed");
		ret = -1;
		goto end;
	}
/*
 * 这里必须要用CONCAT_INFO_KEY来实现info->key，不能这么写 info->##key
 */
#define CONCAT_INFO_KEY(info, key) (info->key)
#define GETSTR(root, key) \
	do {\
		obj = cJSON_GetObjectItemCaseSensitive(root, #key);	\
		if (!cJSON_IsString(obj) || (obj->valuestring == NULL))	\
		{	\
			cjson_error("cJSON_GetObjectItemCaseSensitive %s failed", #key);	\
			ret = -1;	\
			goto end;	\
		}	\
		memcpy(CONCAT_INFO_KEY(info, key), obj->valuestring, strlen(obj->valuestring) + 1);	\
		printf("%s: %s\n", #key, CONCAT_INFO_KEY(info, key));	\
	}while(0)

#define GETNUM(root, key) \
	do {\
		obj = cJSON_GetObjectItemCaseSensitive(root, #key);	\
		if (!cJSON_IsNumber(obj))	\
		{	\
			cjson_error("cJSON_GetObjectItemCaseSensitive %s failed", #key);	\
			ret = -1;	\
			goto end;	\
		}	\
		CONCAT_INFO_KEY(info, key) = obj->valueint;	\
		printf("%s: %d\n", #key, CONCAT_INFO_KEY(info, key));	\
	}while(0)

	GETSTR(dev_info, dev_id);
	GETSTR(dev_info, dev_name);

	boardtemps = cJSON_GetObjectItemCaseSensitive(dev_info, "boardtemp");
	if (boardtemps == NULL)
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive boardtemp failed");
		ret = -1;
		goto end;
	}
	i = 0;
	cJSON_ArrayForEach(boardtemp, boardtemps)	// 遍历数组
	{
		info->boardtemp[i++] = boardtemp->valueint;
		printf("boardtemp[%d]: %d\n", i, boardtemp->valueint);
	}

	voltages = cJSON_GetObjectItemCaseSensitive(dev_info, "voltage");
	if (voltages == NULL)
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive voltage failed");
		ret = -1;
		goto end;
	}
	i = 0;
	cJSON_ArrayForEach(voltage, voltages)
	{
		info->voltage[i++] = voltage->valueint;
		printf("voltage[%d]: %d\n", i, voltage->valueint);
	}

	GETNUM(dev_info, cpu);
	GETNUM(dev_info, memory);
	GETNUM(dev_info, uptime);
	GETNUM(dev_info, time_sync_state);
	GETNUM(dev_info, gps_sync_state);
	GETNUM(dev_info, dev_state);
	GETNUM(dev_info, kickoff);
	GETSTR(dev_info, wifi_ip);
	GETSTR(dev_info, wifi_ssid);
	GETNUM(dev_info, work_mode);
	GETNUM(dev_info, fan_speed);
	GETNUM(dev_info, fan_rpm);
	GETNUM(dev_info, gnss_select);
	GETNUM(dev_info, latitude);
	GETNUM(dev_info, longitude);

	cell_infos = cJSON_GetObjectItemCaseSensitive(root, "cell_info");
	if (cell_infos == NULL)
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive cell_info failed");
		ret = -1;
		goto end;
	}
	i = 0;
	cJSON_ArrayForEach(cell_info, cell_infos)
	{
		obj = cJSON_GetObjectItemCaseSensitive(cell_info, "cell_id");
		if (!cJSON_IsNumber(obj))
		{
			cjson_error("cJSON_GetObjectItemCaseSensitive cell_id failed");
			goto end;
		}
		info->cell_info[i].cell_id = obj->valueint;
		printf("[%d] cell_id: %d\n", i, info->cell_info[i].cell_id);

		obj = cJSON_GetObjectItemCaseSensitive(cell_info, "cfg_state");
		if (!cJSON_IsNumber(obj))
		{
			cjson_error("cJSON_GetObjectItemCaseSensitive cfg_state failed");
			goto end;
		}
		info->cell_info[i].cfg_state = obj->valueint;
		printf("[%d] cfg_state: %d\n", i, info->cell_info[i].cfg_state);

		obj = cJSON_GetObjectItemCaseSensitive(cell_info, "rf_state");
		if (!cJSON_IsNumber(obj))
		{
			cjson_error("cJSON_GetObjectItemCaseSensitive rf_state failed");
			goto end;
		}
		info->cell_info[i].rf_state = obj->valueint;
		printf("[%d] rf_state: %d\n", i, info->cell_info[i].cfg_state);

		obj = cJSON_GetObjectItemCaseSensitive(cell_info, "async_state");
		if (!cJSON_IsNumber(obj))
		{
			cjson_error("cJSON_GetObjectItemCaseSensitive async_state failed");
			goto end;
		}
		info->cell_info[i].async_state = obj->valueint;
		printf("[%d] async_state: %d\n", i, info->cell_info[i].cfg_state);

		i++;
	}

	ret = 0;
end:
	if (ret == -1)
	{
		error_ptr = cJSON_GetErrorPtr();
		if (error_ptr != NULL)
		{
			cjson_error("cause: %s", error_ptr);
		}
	}
	return ret;	
}

static char *opt_json(char *str, json_str_t *info)
{
	cJSON *root, *dev_info, *obj;
	cJSON *boardtemps, *boardtemp;
	cJSON *voltages, *voltage;
	cJSON *cell_infos, *cell_info;
	int i = 0;
	const char *error_ptr;
	char *update_str = NULL;

	root = cJSON_Parse(str);
	if (root == NULL)
	{
		cjson_error("cJSON_Parse failed");
		goto end;
	}

	// dev_info = cJSON_GetObjectItem(root, "dev_info")		// 该函数不区分大小写，可以匹配 Dev_INFO 等
	dev_info = cJSON_GetObjectItemCaseSensitive(root, "dev_info");	// 严格区分大小写
	if (dev_info == NULL)
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive dev_info failed");
		goto end;
	}

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "dev_id");
	if (!cJSON_IsString(obj) || (obj->valuestring == NULL))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive dev_id failed");
		goto end;
	}
	memcpy(info->dev_id, obj->valuestring, strlen(obj->valuestring) + 1);	// 拷贝字符串以及'\0'
	printf("dev_id: %s\n", info->dev_id);

	// 修改 dev_name 为 rrrkkk
	// cJSON_ReplaceItemInObject(root, "name", cJSON_CreateString("rrrkkk"));
	if (!cJSON_ReplaceItemInObjectCaseSensitive(dev_info, "dev_name", cJSON_CreateString("rrrkkk")))
	{
		cjson_error("cJSON_ReplaceItemInObject failed, key：dev_name, value: rrrkkk");
		goto end;
	}

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "dev_name");
	if (!cJSON_IsString(obj) || (obj->valuestring == NULL))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive dev_name failed");
		goto end;
	}
	memcpy(info->dev_name, obj->valuestring, strlen(obj->valuestring) + 1);
	printf("dev_name: %s\n", info->dev_name);

	boardtemps = cJSON_GetObjectItemCaseSensitive(dev_info, "boardtemp");
	if (boardtemps == NULL)
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive boardtemp failed");
		goto end;
	}

	// 修改boardtemp数组第3项为10
	if (!cJSON_ReplaceItemInArray(boardtemps, 2, cJSON_CreateNumber(10)))
	{
		cjson_error("cJSON_ReplaceItemInArray boardtemps failed, index: 2, value: 10");
		goto end;
	}
	i = 0;
	cJSON_ArrayForEach(boardtemp, boardtemps)	// 遍历数组
	{
		info->boardtemp[i++] = boardtemp->valueint;
		printf("boardtemp[%d]: %d\n", i, boardtemp->valueint);
	}

	voltages = cJSON_GetObjectItemCaseSensitive(dev_info, "voltage");
	if (voltages == NULL)
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive voltage failed");
		goto end;
	}
	i = 0;
	cJSON_ArrayForEach(voltage, voltages)
	{
		info->voltage[i++] = voltage->valueint;
		printf("voltage[%d]: %d\n", i, voltage->valueint);
	}

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "cpu");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive cpu failed");
		goto end;
	}
	info->cpu = obj->valueint;
	printf("cpu: %d\n", info->cpu);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "memory");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive memory failed");
		goto end;
	}
	info->memory = obj->valueint;
	printf("memory: %d\n", info->memory);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "uptime");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive uptime failed");
		goto end;
	}
	info->uptime = obj->valueint;
	printf("uptime: %d\n", info->uptime);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "time_sync_state");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive time_sync_state failed");
		goto end;
	}
	info->time_sync_state = obj->valueint;
	printf("time_sync_state: %d\n", info->time_sync_state);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "gps_sync_state");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive gps_sync_state failed");
		goto end;
	}
	info->gps_sync_state = obj->valueint;
	printf("gps_sync_state: %d\n", info->gps_sync_state);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "dev_state");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive dev_state failed");
		goto end;
	}
	info->dev_state = obj->valueint;
	printf("dev_state: %d\n", info->dev_state);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "kickoff");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive kickoff failed");
		goto end;
	}
	info->kickoff = obj->valueint;
	printf("kickoff: %d\n", info->kickoff);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "wifi_ip");
	if (!cJSON_IsString(obj) || (obj->valuestring == NULL))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive wifi_ip failed");
		goto end;
	}
	memcpy(info->wifi_ip, obj->valuestring, strlen(obj->valuestring) + 1);
	printf("wifi_ip: %s\n", info->wifi_ip);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "wifi_ssid");
	if (!cJSON_IsString(obj) || (obj->valuestring == NULL))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive wifi_ssid failed");
		goto end;
	}
	memcpy(info->wifi_ssid, obj->valuestring, strlen(obj->valuestring) + 1);
	printf("wifi_ssid: %s\n", info->wifi_ssid);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "work_mode");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive work_mode failed");
		goto end;
	}
	info->work_mode = obj->valueint;
	printf("work_mode: %d\n", info->work_mode);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "fan_speed");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive fan_speed failed");
		goto end;
	}
	info->fan_speed = obj->valueint;
	printf("fan_speed: %d\n", info->fan_speed);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "fan_rpm");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive fan_rpm failed");
		goto end;
	}
	info->fan_rpm = obj->valueint;
	printf("fan_rpm: %d\n", info->fan_rpm);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "gnss_select");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive gnss_select failed");
		goto end;
	}
	info->gnss_select = obj->valueint;
	printf("gnss_select: %d\n", info->gnss_select);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "latitude");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive latitude failed");
		goto end;
	}
	info->latitude = obj->valueint;
	printf("latitude: %d\n", info->latitude);

	obj = cJSON_GetObjectItemCaseSensitive(dev_info, "longitude");
	if (!cJSON_IsNumber(obj))
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive longitude failed");
		goto end;
	}
	info->longitude = obj->valueint;
	printf("longitude: %d\n", info->longitude);

	cell_infos = cJSON_GetObjectItemCaseSensitive(root, "cell_info");
	if (cell_infos == NULL)
	{
		cjson_error("cJSON_GetObjectItemCaseSensitive cell_info failed");
		goto end;
	}
	i = 0;
	cJSON_ArrayForEach(cell_info, cell_infos)
	{
		obj = cJSON_GetObjectItemCaseSensitive(cell_info, "cell_id");
		if (!cJSON_IsNumber(obj))
		{
			cjson_error("cJSON_GetObjectItemCaseSensitive cell_id failed");
			goto end;
		}
		info->cell_info[i].cell_id = obj->valueint;
		printf("[%d] cell_id: %d\n", i, info->cell_info[i].cell_id);

		obj = cJSON_GetObjectItemCaseSensitive(cell_info, "cfg_state");
		if (!cJSON_IsNumber(obj))
		{
			cjson_error("cJSON_GetObjectItemCaseSensitive cfg_state failed");
			goto end;
		}
		info->cell_info[i].cfg_state = obj->valueint;
		printf("[%d] cfg_state: %d\n", i, info->cell_info[i].cfg_state);

		obj = cJSON_GetObjectItemCaseSensitive(cell_info, "rf_state");
		if (!cJSON_IsNumber(obj))
		{
			cjson_error("cJSON_GetObjectItemCaseSensitive rf_state failed");
			goto end;
		}
		info->cell_info[i].rf_state = obj->valueint;
		printf("[%d] rf_state: %d\n", i, info->cell_info[i].cfg_state);

		obj = cJSON_GetObjectItemCaseSensitive(cell_info, "async_state");
		if (!cJSON_IsNumber(obj))
		{
			cjson_error("cJSON_GetObjectItemCaseSensitive async_state failed");
			goto end;
		}
		info->cell_info[i].async_state = obj->valueint;
		printf("[%d] async_state: %d\n", i, info->cell_info[i].cfg_state);

		i++;
	}

	update_str = cJSON_Print(root);
	if (str == NULL)
	{
		cjson_error("cJSON_Print failed");
		goto end;
	}

end:
	if (update_str == NULL)
	{
		error_ptr = cJSON_GetErrorPtr();
		if (error_ptr != NULL)
		{
			cjson_error("cause: %s", error_ptr);
		}
	}
	return update_str;	
}

/*
void replace_json(void)
{
	// array
	cJSON_ReplaceItemInArray		// 通过索引替换数组中的一个项
	cJSON_ReplaceItemViaPointer		// 通过元素的指针来替换,如果失败返回0，内部操作是分离旧项，删除它，然后在原位置插入新项

	// object
	cJSON_ReplaceItemInObjectCaseSensitive 	// 通过键来替换
	cJSON_ReplaceItemViaPointer 	// 传入指定元素的指针来替换，如果失败返回0，内部操作是分离旧项，删除它，然后在原位置插入新项

	"name":	"小明"		====>	"name":	"小红"
	// 使用cJSON_ReplaceItemInObject可以直接进行修改
	cJSON_ReplaceItemInObject(root, "name", cJSON_CreateString("小红"));


	"age": 23		====>	"age": 20
	cJSON_ReplaceItemInObject(root, "age", cJSON_CreateNumber(20));


	"vip": true	 ====>  "vip": false
	// 使用cJSON_ReplaceItemInObject可以直接进行修改
	cJSON_ReplaceItemInObject(root, "vip", cJSON_CreateBool(false));
}
*/
